// Copyright (c) 2023, Cogent Core. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package typegen

import (
	"os"
	"strings"
	"testing"
	"text/template"

	"cogentcore.org/core/base/ordmap"
	"cogentcore.org/core/base/strcase"
	"cogentcore.org/core/cli"
	"cogentcore.org/core/types"
	"cogentcore.org/core/types/typegen/testdata"
	"github.com/stretchr/testify/assert"
)

func TestGenerate(t *testing.T) {
	c := &Config{
		AddMethods: true,
		AddFuncs:   true,
		InterfaceConfigs: ordmap.Make([]ordmap.KeyValue[string, *Config]{{
			"fmt.Stringer", &Config{
				AddTypes: true,
				Setters:  true,
				Templates: []*template.Template{
					template.Must(template.New("Stringer").Parse(`
					func (t *{{.LocalName}}) MyCustomFuncForStringers(a any) error {
						return nil
					}`)),
				},
			},
		}}),
	}
	err := cli.SetFromDefaults(c)
	if err != nil {
		t.Errorf("programmer error: error setting config from default tags: %v", err)
	}
	c.Dir = "./testdata"
	err = Generate(c)
	if err != nil {
		t.Errorf("error while generating: %v", err)
	}
	have, err := os.ReadFile("testdata/typegen.go")
	if err != nil {
		t.Errorf("error while reading generated file: %v", err)
	}
	want, err := os.ReadFile("testdata/typegen.golden")
	if err != nil {
		t.Errorf("error while reading golden file: %v", err)
	}
	// ignore first line, which has "Code generated by" message
	// that can change based on where go test is ran.
	_, shave, got := strings.Cut(string(have), "\n")
	if !got {
		t.Errorf("expected string with newline in testdata/typegen.go, but got %q", have)
	}
	_, swant, got := strings.Cut(string(want), "\n")
	if !got {
		t.Errorf("expected string with newline in testdata/typegen.golden, but got %q", want)
	}
	swant = strings.ReplaceAll(swant, "\r\n", "\n")
	if shave != swant {
		t.Errorf("expected generated file and expected file to be the same after the first line, but they are not (compare ./testdata/typegen.go and ./testdata/typegen.golden to see the difference)")
	}
}

func TestPerson(t *testing.T) {
	want := types.For[testdata.Person]()
	have := types.TypeByName("cogentcore.org/core/types/typegen/testdata.Person")
	assert.Equal(t, want, have)
	have = types.TypeByValue(testdata.Person{})
	assert.Equal(t, want, have)
	if _, ok := have.Instance.(*testdata.Person); !ok {
		t.Errorf("expected instance to be a Person, but it is a %T (value %v)", have.Instance, have.Instance)
	}
	assert.Equal(t, "cogentcore.org/core/types/typegen/testdata.Person", have.Name)
	assert.Equal(t, 2, len(have.Directives))
	assert.Equal(t, 5, len(have.Fields))
	assert.Equal(t, 1, len(have.Embeds))
	assert.Equal(t, 1, len(have.Methods))
}

func BenchmarkIDName(b *testing.B) {
	const path = "cogentcore.org/core/core.Button"
	for i := 0; i < b.N; i++ {
		li := strings.LastIndex(path, ".")
		_ = strcase.ToKebab(path[li+1:])
	}
}
